<?php
namespace App\Common\Cla\EChat\Room\ReceivePools;

use App\Common as com;
use App\Common\Cla\EChat\Room\ReceivePools\Item as thisItem;
use App\Common\Cla\EChat\Room as nameRoom;

class Item extends com\Cla\ClaBase {

	/**
	 * @var nameRoom
	 */
	private nameRoom $room;

	/**
	 * @var int
	 */
	private int $receiveId;

	/**
	 * @var int|null
	 */
	private int|null $receiveTimeoutS;

	/**
	 * @var int|null
	 */
	private int|null $expiresTime;

	/**
	 * 开始时间
	 * @var int
	 */
	private int $startTime;

	/**
	 * @var \Closure
	 */
	private \Closure $callback;

	/**
	 * @var \Swoole\Coroutine\Channel[]
	 */
	private array $waitReceiveChannelPools = [];

	/**
	 * @var int
	 */
	private int $waitReceiveChannelPoolsIndex = 0;

	/**
	 * 是否已停止接收
	 * @var bool
	 */
	private bool $isStopReceived = false;

	/**
	 * 停止接收返回的内容
	 * @var mixed
	 */
	private mixed $stopReceivedRes = null;

	/**
	 * @param nameRoom $room
	 * @param int $receiveId
	 * @param \Closure $callback
	 * @param int $receiveTimeoutS [-1]永久
	 */
	public function __construct( nameRoom $room, int $receiveId, \Closure $callback, int $receiveTimeoutS ) {
		$this->room = $room;
		$this->receiveId = $receiveId;
		$this->callback = $callback;
		$this->receiveTimeoutS = $receiveTimeoutS;
		//
		$this->startTime = com\Lib\Fun\Time::getTime();
		//
		if( $receiveTimeoutS === -1 ){
			$this->expiresTime = -1;
		}else{
			$this->expiresTime = $this->startTime + $receiveTimeoutS;
		}

		//
	    parent::__construct();
	}

	public function getRoom(  ):nameRoom {
		return $this->room;
	}

	public function getReceiveId(  ):int {
		return $this->receiveId;
	}

	public function getCallback(  ):\Closure {
		return $this->callback;
	}

	public function getReceiveTimeoutS(  ):int|null {
		return $this->receiveTimeoutS;
	}

	public function getExpiresTime(  ):int|null {
		return $this->expiresTime;
	}

	/**
	 * 获取开始时间
	 * @return int
	 */
	public function getStartTime(  ):int {
		return $this->startTime;
	}

	/**
	 * 是否过期
	 * @return bool
	 */
	public function isExpires(  ):bool {
		if( $this->expiresTime === -1 ){
			return false;
		}
		//
		return com\Lib\Fun\Time::getTime() > $this->expiresTime;
	}

	/**
	 * 等待接收结束
	 * @param int $waitTimeoutS 等待超时时间。单位：秒。[-1]最大等待时间
	 * @return mixed 是否已结束 [其他]正常结束，并返回fn的返回数据 [fasle]超时或已关闭
	 */
	public function waitReceive( int $waitTimeoutS = -1 ):mixed {
		// 取最大超时时间
		if( $this->getReceiveTimeoutS() >= 0 ){
			$maxWaitTimeS = $this->getExpiresTime() - com\Lib\Fun\Time::getTime();
			if( $maxWaitTimeS < 0 ){
				$maxWaitTimeS = 0;
			}
		}else{
			$maxWaitTimeS = -1;
		}
		// 使用最短超时时间
		if( $waitTimeoutS >= 0 ){
			if( $maxWaitTimeS > $waitTimeoutS ){
				$maxWaitTimeS = $waitTimeoutS;
			}
		}
		// 检查是否已结束，已经结束则直接返回数据
		if( $this->isStopReceived ){
			return $this->stopReceivedRes;
		}
		//
		if( $maxWaitTimeS !== 0 ){
			$waitReceiveChannel = new \Swoole\Coroutine\Channel();
			//
			$waitReceiveChannelPoolsIndex = ++$this->waitReceiveChannelPoolsIndex;
			$this->waitReceiveChannelPools[ $waitReceiveChannelPoolsIndex ] = $waitReceiveChannel;
			//
			$res = $waitReceiveChannel->pop( $maxWaitTimeS );
			// 删除等待通道
			unset( $this->waitReceiveChannelPools[ $waitReceiveChannelPoolsIndex ] );
		}
		//
		return $res ?? null;
	}

	/**
	 * 等待接收结束，并在完成或超时时如果没其他等待则结束接收
	 * @param int|null $waitTimeoutS 等待超时时间。单位：秒
	 * @return mixed 是否已结束 [其他]正常结束，并返回fn的返回数据 [fasle]超时或已关闭
	 */
	public function waitReceiveIfWaitEmptyThatStop( ?int $waitTimeoutS = null ):mixed {
		$res = $this->waitReceive( $waitTimeoutS );
		//
		if( empty( $this->waitReceiveChannelPools ) ){
			$this->stopReceive();
		}
		//
		return $res;
	}

	/**
	 * 停止接收消息
	 * @param mixed $reData 结束通知数据
	 * @return bool [true][必返]
	 */
	public function ifWaitEmptyThatStopReceive( mixed $reData = null ):bool {
		foreach( $this->waitReceiveChannelPools as $waitReceiveChannel ){
			$waitReceiveChannel->push( $reData );
		}
		//
		$this->getRoom()->getReceivePools()->rm( $this->getReceiveId() );
		//
		return true;
	}

	/**
	 * 是否已停止接收消息
	 * @return bool 是否已停止接收消息
	 */
	public function isStopReceive():bool {
		return $this->isStopReceived;
	}

	/**
	 * 获取结束的通知数据
	 * @return mixed
	 */
	public function getStopReceivedRes():mixed {
		return $this->stopReceivedRes;
	}

	/**
	 * 停止接收消息
	 * @param mixed $reData 结束通知数据
	 * @return bool [true][必返]
	 */
	public function stopReceive( mixed $reData = null ):bool {
		$this->isStopReceived = true;
		$this->stopReceivedRes = $reData;
		//
		foreach( $this->waitReceiveChannelPools as $waitReceiveChannel ){
			$waitReceiveChannel->push( $reData );
		}
		//
		$this->getRoom()->getReceivePools()->rm( $this->getReceiveId() );
		//
		return true;
	}

	// ===============================
	//
	//
	// 下面为静态方法
	//
	//
	// ===============================

	/**
	 * @param nameRoom $room
	 * @param int $receiveId
	 * @param \Closure $callback
	 * @return $this
	 */
	public static function makeOfNew( nameRoom $room, int $receiveId, \Closure $callback, ?int $receiveTimeoutS = null ):self {
	    return self::make( $room, $receiveId, $callback, $receiveTimeoutS );
	}

}
